class: inverse,left, middle background-image: url(data:image/png;base64,#background.png) background-size: cover <img src="data:image/png;base64,#LOGO_DIPLOMADO.png" width="500px"/> ##Módulo 4: Procesamiento masivo de datos geoespaciales ###Google Earth Engine: Aplicaciones en GEE José A. Lastra<br> <a href="http://github.com/JoseLastra"> Github: JoseLastra</a><br> <a href="mailto:jose.lastra@pucv.cl"> jose.lastra@pucv.cl</a><br> .large[<b><a href="https://www.pucv.cl/uuaa/site/edic/base/port/labgrs.html">LabGRS</a> | Noviembre 2022</b>] <br> --- class: center,middle background-image: url(data:image/png;base64,#labgrs_logo.png) background-size: 35% --- ## Contenidos .pull-left[ - Trabajo con colecciones: * Importación * Filtros temporales y espaciales * Creación de una imagen * Exportación de datos - Funciones en GEE * Uso de **map()** - Reducciones * Temporales * Espaciales - Extracción series de tiempo ] .pull-right[ <img src="data:image/png;base64,#https://earthengine.google.com/static/images/earth-engine-logo.png" width="300px"/> ] --- ## Series de tiempo - GEE tiene disponible un gran número de datasets que disponen de información estandarizada en el tiempo, muy útil para análisis de cambios y dinámica espacio-temporal. - Por ejemplo: * GIMMS NDVI (Jul 1981 – Dec 2013), * Hybrid Coordinate Ocean Model (Oct 1992 – actualidad), * MOD13Q1/MYD13Q1 Versión 6 Vegetation Indices 16-Day L3 Global 250 m (Feb 18, 2000 - actualidad), * Landsat 5/7/8/9 (Ene 1984 - actualidad) - Además de información climática de diferentes modelos - Toda esta información es mannipulable y nos permite hacer diferentes análisis además de extraer información. --- ## Extracción de series de tiempo - Para este ejercicio, aplicaremos algunas herramientas ya usadas: **(i) uso de map()** y **(ii) agregación espacial y temporal** - Cargaremos la colección 2<sup>1</sup> de Landsat 5, 8 y 9 L2 (Surface reflectance) y calcularemos el **Enhanced Vegetation Index** para todas las imágenes disponibles. - Para filtrar las colecciones haremos uso de una geometrÃa y consideraremos todas las escenas que tengan 70% o menos de cobertura nubosa. - **Importante**: se puede considerar un umbral más alto o más bajo dependiendo de la zona de estudio y la cantidad de pérdida de datos. .footnote[ <sup>1</sup> La información de la colección 1 será dada de baja a finales de este año y desaparecerá de todos los catálogos. [**Más información**](https://www.usgs.gov/landsat-missions/landsat-collection-1) ] --- ## Preparación de funciones -- - Lo primero a preparar, son las funciones para trabajar con los datos de la colección 2 y los múltiples sensores: * Función para estandarizar nombres de bandas * Enmascarado de nubes * Reescalamiento de datos ([**más información**](https://www.usgs.gov/landsat-missions/landsat-collection-2-level-2-science-products)) * Armonización entre sensores ([**más información**](https://openprairie.sdstate.edu/cgi/viewcontent.cgi?referer=https://scholar.google.com/&httpsredir=1&article=1035&context=gsce_pubs)) * Cálculo del Ãndice -- ```js // funciones para seleccionar y renombrar bandas // Landsat 8 y 9 var renameOli = function (img) { return img.select( ['SR_B2', 'SR_B3', 'SR_B4', 'SR_B5', 'SR_B6', 'SR_B7', 'QA_PIXEL'], ['azul', 'verde', 'rojo', 'nir', 'swir1', 'swir2', 'pixel_qa']); }; // Landsat 5 y 7 var renameEtm = function (img) { return img.select( ['SR_B1', 'SR_B2', 'SR_B3', 'SR_B4', 'SR_B5', 'SR_B7', 'QA_PIXEL'], ['azul', 'verde', 'rojo', 'nir', 'swir1', 'swir2', 'pixel_qa']); }; ``` <script type="text/javascript"> // funciones para seleccionar y renombrar bandas // Landsat 8 y 9 var renameOli = function (img) { return img.select( ['SR_B2', 'SR_B3', 'SR_B4', 'SR_B5', 'SR_B6', 'SR_B7', 'QA_PIXEL'], ['azul', 'verde', 'rojo', 'nir', 'swir1', 'swir2', 'pixel_qa']); }; // Landsat 5 y 7 var renameEtm = function (img) { return img.select( ['SR_B1', 'SR_B2', 'SR_B3', 'SR_B4', 'SR_B5', 'SR_B7', 'QA_PIXEL'], ['azul', 'verde', 'rojo', 'nir', 'swir1', 'swir2', 'pixel_qa']); }; </script> --- ## Preparación de funciones -- - **Importante:** la aplicación de quality assesment sobre nuestros datos, dependerá de la zona, el nivel de rigurosidad que queramos en la limpieza, la cantidad de información que estemos dispuesto a perder, entre muchos otros factores. -- - Enmascarado de datos Landsat 5-7 ```js /* limpieza con pixel QA Landsat 4-7 */ var l5Clouds = function (img){ var pixel_qa = img.select('pixel_qa'); var cl1 = pixel_qa.eq(1);// Fill var cl2 = pixel_qa.eq(5440);//Clear with low sets var mask = cl1.or(cl2); //actualiza información que se mantendrá en el dato de salida por pixel return img.updateMask(mask); }; ``` <script type="text/javascript"> /* limpieza con pixel QA Landsat 4-7 */ var l5Clouds = function (img){ var pixel_qa = img.select('pixel_qa'); var cl1 = pixel_qa.eq(1);// Fill var cl2 = pixel_qa.eq(5440);//Clear with low sets var mask = cl1.or(cl2); //actualiza información que se mantendrá en el dato de salida por pixel return img.updateMask(mask); }; </script> - Basado en: [**Science Product Guide**](https://d9-wret.s3.us-west-2.amazonaws.com/assets/palladium/production/s3fs-public/media/files/LSDS-1618_Landsat-4-7_C2-L2-ScienceProductGuide-v4.pdf) Versión Marzo 2022 --- ## Preparación de funciones -- - Enmascarado de datos Landsat 8-9 ```js /* limpieza con pixel QA Landsat 8-9 */ var l8Clouds = function (img){ var pixel_qa = img.select('pixel_qa'); var cl1 = pixel_qa.eq(1); // Fill var cl2 = pixel_qa.eq(21824); //Clear with low sets var mask = cl1.or(cl2); //actualiza información que se mantendrá en el dato de salida por pixel return img.updateMask(mask); }; ``` <script type="text/javascript"> /* limpieza con pixel QA Landsat 8-9 */ var l8Clouds = function (img){ var pixel_qa = img.select('pixel_qa'); var cl1 = pixel_qa.eq(1); // Fill var cl2 = pixel_qa.eq(21824); //Clear with low sets var mask = cl1.or(cl2); //actualiza información que se mantendrá en el dato de salida por pixel return img.updateMask(mask); }; </script> - Basado en: [**Science Product Guide**](https://d9-wret.s3.us-west-2.amazonaws.com/assets/palladium/production/s3fs-public/media/files/LSDS-1619_Landsat-8-9-C2-L2-ScienceProductGuide-v4.pdf) Versión marzo 2022 --- ## Preparación de funciones -- - Re-escalado de datos ```js // aplicación de factores de escala var applyScaleFactors = function (image) { var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2); return image.addBands(opticalBands, null, true); }; ``` <script type="text/javascript"> // aplicación de factores de escala var applyScaleFactors = function (image) { var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2); return image.addBands(opticalBands, null, true); }; </script> -- - Solo trabajaremos con la información óptica, pero el escalado también puede aplicarse a la información termal. --- ## Preparación de funciones -- - Armonización entre sensores -- - *"Traducción"* de reflectancias **TM/ETM+** a **OLI** ```js // coeficientes provistos por Roy et al. (2016) // SR 5/7 a OLI SR var coefficients = { itcps: ee.Image.constant([0.0003, 0.0088, 0.0061, 0.0412, 0.0254, 0.0172]), slopes: ee.Image.constant([0.8474, 0.8483, 0.9047, 0.8462, 0.8937, 0.9071]) }; var etmToOli = function (img) { return img.select(['azul', 'verde', 'rojo', 'nir', 'swir1', 'swir2']) .multiply(coefficients.slopes) .add(coefficients.itcps) .addBands(img.select('pixel_qa')); }; ``` <script type="text/javascript"> // coeficientes provistos por Roy et al. (2016) // SR 5/7 a OLI SR var coefficients = { itcps: ee.Image.constant([0.0003, 0.0088, 0.0061, 0.0412, 0.0254, 0.0172]), slopes: ee.Image.constant([0.8474, 0.8483, 0.9047, 0.8462, 0.8937, 0.9071]) }; var etmToOli = function (img) { return img.select(['azul', 'verde', 'rojo', 'nir', 'swir1', 'swir2']) .multiply(coefficients.slopes) .add(coefficients.itcps) .addBands(img.select('pixel_qa')); }; </script> --- ## Preparación de funciones -- - Cálculo de EVI ```js //Función para calcular EVI var EVI = function(image) { var evi = image.expression('2.5 * (nir - rojo) / (nir + 6 * rojo - 7.5 * azul + 1)', { 'nir': image.select('nir'), 'rojo': image.select('rojo'), 'azul': image.select('azul')}).rename('EVI'); return image.addBands(evi); }; ``` <script type="text/javascript"> //Función para calcular EVI var EVI = function(image) { var evi = image.expression('2.5 * (nir - rojo) / (nir + 6 * rojo - 7.5 * azul + 1)', { 'nir': image.select('nir'), 'rojo': image.select('rojo'), 'azul': image.select('azul')}).rename('EVI'); return image.addBands(evi); }; </script> --- ## Preparación de funciones -- - Con las funciones creadas, ahora las incorporaremos a dos funciones: **prepOLI()** y **prepETM()** ```js // Preparación OLI var prepOli =function (img) { var orig = img; img = applyScaleFactors(img); img = renameOli(img); img = l8Clouds(img); img = EVI(img); return ee.Image(img.copyProperties(orig, orig.propertyNames())); }; // Preparacion TM/ETM+ var prepEtm = function(img) { var orig = img; img = applyScaleFactors(img); img = renameEtm(img); img = l5Clouds(img); img = etmToOli(img); //armonización img = EVI(img); return ee.Image(img.copyProperties(orig, orig.propertyNames())); }; ``` <script type="text/javascript"> // Preparación OLI var prepOli =function (img) { var orig = img; img = applyScaleFactors(img); img = renameOli(img); img = l8Clouds(img); img = EVI(img); return ee.Image(img.copyProperties(orig, orig.propertyNames())); }; // Preparacion TM/ETM+ var prepEtm = function(img) { var orig = img; img = applyScaleFactors(img); img = renameEtm(img); img = l5Clouds(img); img = etmToOli(img); //armonización img = EVI(img); return ee.Image(img.copyProperties(orig, orig.propertyNames())); }; </script> --- ## Aplicando funciones a nuestras colecciones ```js //aplicando funciones a colecciones var L5sr = ee.ImageCollection("LANDSAT/LT05/C02/T1_L2") .filterBounds(roi) .filter(ee.Filter.lt('CLOUD_COVER',70)) .map(prepEtm); var L7sr = ee.ImageCollection("LANDSAT/LE07/C02/T1_L2") .filterBounds(roi) .filter(ee.Filter.lt('CLOUD_COVER',70)) .map(prepEtm); var L8sr = ee.ImageCollection("LANDSAT/LC08/C02/T1_L2") .filterBounds(roi) .filter(ee.Filter.lt('CLOUD_COVER',70)) .map(prepOli); var L9sr = ee.ImageCollection("LANDSAT/LC09/C02/T1_L2") .filterBounds(roi) .filter(ee.Filter.lt('CLOUD_COVER',70)) .map(prepOli); ``` <script type="text/javascript"> //aplicando funciones a colecciones var L5sr = ee.ImageCollection("LANDSAT/LT05/C02/T1_L2") .filterBounds(roi) .filter(ee.Filter.lt('CLOUD_COVER',70)) .map(prepEtm); var L7sr = ee.ImageCollection("LANDSAT/LE07/C02/T1_L2") .filterBounds(roi) .filter(ee.Filter.lt('CLOUD_COVER',70)) .map(prepEtm); var L8sr = ee.ImageCollection("LANDSAT/LC08/C02/T1_L2") .filterBounds(roi) .filter(ee.Filter.lt('CLOUD_COVER',70)) .map(prepOli); var L9sr = ee.ImageCollection("LANDSAT/LC09/C02/T1_L2") .filterBounds(roi) .filter(ee.Filter.lt('CLOUD_COVER',70)) .map(prepOli); </script> --- ## Fusión de colecciones -- - Con nuestros datos preparados, podemos fusionar las colecciones en una sola **ImageCollection** -- - Esta fusión de colecciones se puede hacer con la función **merge()** -- - Considerar la función **sort()** para ordenar temporalmente nuestras imágenes dentro de la nueva colección ```js var full = L5sr.merge(L7sr).merge(L8sr).merge(L9sr).sort('system:time_start'); print(full,'colección completa'); ``` <script type="text/javascript"> var full = L5sr.merge(L7sr).merge(L8sr).merge(L9sr).sort('system:time_start'); print(full,'colección completa'); </script> <center><img src="data:image/png;base64,#img20.png" width="550px"/></center> --- ## Visualizando EVI -- - Añadiremos la información al mapa para poder inspeccionar los valores. ```js // Visualización var EVI_params = {min:0, max:1, palette: [ 'FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718', '74A901', '66A000', '529400', '3E8601', '207401', '056201', '004C00', '023B01', '012E01', '011D01', '011301' ], bands: 'EVI'}; Map.addLayer(full.first(),EVI_params,'EVI vis'); ``` <script type="text/javascript"> // Visualización var EVI_params = {min:0, max:1, palette: [ 'FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718', '74A901', '66A000', '529400', '3E8601', '207401', '056201', '004C00', '023B01', '012E01', '011D01', '011301' ], bands: 'EVI'}; Map.addLayer(full.first(),EVI_params,'EVI vis'); </script> --- class: middle <center><img src="data:image/png;base64,#img21.png" width="550px"/></center> --- ## Construcción series de tiempo -- - Crearemos un polÃgono de nombre **zona1** para la extracción mediana de un área de interés. -- - Emplearemos la función **ui.Chart.image.seriesByRegion()** ```js // Gráfico de serie de tiempo. var eviTimeSeries = ui.Chart.image.seriesByRegion(//gráfico utilizando región de referencia full, zona1, ee.Reducer.median(), 'EVI', 30, 'system:time_start','label') //agregación de mediana .setChartType('ScatterChart')//tipo de gráfico .setOptions({//opciones title: 'EVI',//tÃtulo de gráfico vAxis: {title: 'EVI median'},//eje y lineWidth: 1, pointSize: 2.5, series: { 0: {color: '#4e00f4'}, }}); // Display. print(eviTimeSeries, 'serie de tiempo ejemplo'); ``` <script type="text/javascript"> // Gráfico de serie de tiempo. var eviTimeSeries = ui.Chart.image.seriesByRegion(//gráfico utilizando región de referencia full, zona1, ee.Reducer.median(), 'EVI', 30, 'system:time_start','label') //agregación de mediana .setChartType('ScatterChart')//tipo de gráfico .setOptions({//opciones title: 'EVI',//tÃtulo de gráfico vAxis: {title: 'EVI median'},//eje y lineWidth: 1, pointSize: 2.5, series: { 0: {color: '#4e00f4'}, }}); // Display. print(eviTimeSeries, 'serie de tiempo ejemplo'); </script> --- class: middle <center><img src="data:image/png;base64,#img22.png" width="950px"/></center> --- ## Construcción series de tiempo -- - También podemos sacar series de tiempo para múltiples polÃgonos -- - Veamos un ejemplo con nuestro asset de nombre **zonas** ```js var plotEVI = ui.Chart.image.seriesByRegion( full, zonas, ee.Reducer.median(),'EVI',30) .setChartType('LineChart') .setSeriesNames(['Pradera','Nativo en rÃo','Paltos','Agua']) .setOptions({ lineWidth: 1, pointSize: 2, colors: ['#FA0707', '#087605','#97D000','#148CC4'], title: 'EVI median', vAxis: {title: 'EVI'} }); print(plotEVI, 'Múltiples polÃgonos'); ``` <script type="text/javascript"> var plotEVI = ui.Chart.image.seriesByRegion( full, zonas, ee.Reducer.median(),'EVI',30) .setChartType('LineChart') .setSeriesNames(['Pradera','Nativo en rÃo','Paltos','Agua']) .setOptions({ lineWidth: 1, pointSize: 2, colors: ['#FA0707', '#087605','#97D000','#148CC4'], title: 'EVI median', vAxis: {title: 'EVI'} }); print(plotEVI, 'Múltiples polÃgonos'); </script> --- class: middle <center><img src="data:image/png;base64,#img23.png" width="950px"/></center> --- ## BibliografÃa complementaria - Earth Engine Code Editor | Google Earth Engine |. (s.f.). Google Developers. https://developers.google.com/earth-engine/guides/playground - Gorelick, N., Hancher, M., Dixon, M., Ilyushchenko, S., Thau, D., & Moore, R. (2017). Google Earth Engine: Planetary-scale geospatial analysis for everyone. Remote sensing of Environment, 202, 18-27. [Ver](https://www.sciencedirect.com/science/article/pii/S0034425717302900) - Mutanga, O., & Kumar, L. (2019). Google earth engine applications. Remote Sensing, 11(5), 591. [Ver](https://www.mdpi.com/2072-4292/11/5/591/htm) - Zhao, Q., Yu, L., Li, X., Peng, D., Zhang, Y., & Gong, P. (2021). Progress and trends in the application of Google Earth and Google Earth Engine. Remote Sensing, 13(18), 3778. [Ver](https://www.mdpi.com/2072-4292/13/18/3778) --- class: inverse middle 